#include "global.h"
#include "IO.h"
#include <cassert>
#include <cstdio>
#include "write.h"

VirtualDisk vdisk;
int VirtualDisk::first_available_st(int size,int subpartid,int stpoint,int step)
{
    // assert(subpart_load[subpartid]+size<=V);
    // subpart_load[subpartid]+=size;

    // if(stpoint<1||stpoint>V) fprintf(stderr,"%d\n",stpoint);
    int x=stpoint;
    while(1)
    {
        int partid=findpart(x);
        int L=(partid*partsize+1),R=(partid+1)*partsize;
        int sid=step>0?R-x:x-L;

        int realdisk,realpart;
        mapto_real(realdisk,realpart,partid,subpartid,1);
        int realpos=(realpart*partsize+1)+(x-L);

        int count=0;
        for(int i=0;i <= sid && i >= -sid; i+=step)
        {
            if(disk[realdisk][point_away(realpos,i)] == 0) count++;
            if(count==size) return x;
        }
        x=step>0?R+1:L-1;
        if(x<1) x+=V;
        if(x>V) x-=V;
        // if(x<1||x>V) fprintf(stderr,"%d %d %d %d\n",x,partid,L,R);
    }
}
void VirtualDisk::mapto_real(int &realdisk,int &realpart,int partid,int subpartid,int repid)
{
    int listpos=subpartid*30 + (repid-1)*10 + partid;
    realdisk=listpos/9+1,realpart=listpos%9;
}
void VirtualDisk::object_in(int &object_id,int &num,int x,int subpartid)
{
    int partid=findpart(x),realdisk,realpart;
    mapto_real(realdisk,realpart,partid,subpartid,1);
    int L=(partid*partsize+1),R=(partid+1)*partsize;
    
    int realx=(x-L)+(realpart*partsize+1);
    object_id=disk[realdisk][realx],num=unit_num[realdisk][realx];
}
void VirtualDisk::linear_detector(int object_id,int stpoint,int side,int step)
{
    int subpartid = object_id%3;
    int x=first_available_st(object[object_id].size,subpartid, stpoint,step);
    assert(x>=1&&x<=V);

    int current_write_point=0;
    for(int i=0;i < V && i > -V; i+=step,x=(x-1+V+step)%V+1)
    {
        int partid=findpart(x);
        int L=(partid*partsize+1),R=(partid+1)*partsize;

        current_write_point++;
        // memset(object[object_id].replica,0,sizeof(object[object_id].replica));
        for(int j=1;j<=REP_NUM;j++)
        {
            int realdisk,realpart;
            mapto_real(realdisk,realpart,partid,subpartid,j);
            int realpos=(realpart*partsize+1)+(x-L);
            if(disk[realdisk][realpos] != 0)
            {
                assert(j==1);//means parts are inconsistent if assert.
                current_write_point--;
                break;
            }

            assert(object[object_id].replica[j]==0||object[object_id].replica[j]==realdisk);

            object[object_id].replica[j]=realdisk;
            object[object_id].unit[j][current_write_point]=realpos;

            disk[realdisk][realpos]=object_id;
            unit_num[realdisk][realpos]=current_write_point;
        }
        if(current_write_point==object[object_id].size) return ; 
    }
    assert(current_write_point==object[object_id].size);
}

const double _bk[6]={0,15/60.0,29/60.0,41/60.0,53/60.0,60/60.0};
void do_object_write(int object_id)
{
    int size=object[object_id].size;
    int st=_bk[size-1]*vdisk.V+1,ed=_bk[size]*vdisk.V;
    int start=(size&1)?st:ed;
    int side=(size&1)?ed:st;
    int step=(size&1)?1:-1;
    vdisk.linear_detector(object_id,start,side,step);
}
void write_action(int round, int time)
{
    if(time==1) vdisk=VirtualDisk(N,V);
    if(round>=2){
        write_action2();
        return;
    }
    int n_write;
    if (round == 1) {
        n_write = getint();
        n_writes[time] = n_write;
    } else {
        n_write = n_writes[time];
    }

    for (int i = 1; i <= n_write; i++) {
        int id, size, tag;
        if (round == 1) {
            id=getint();size=getint();tag=getint();
            write_datas[time].emplace_back(id, size, tag);
        } else {
            const auto& write_tuple = write_datas[time].at(i - 1);
            id = get<0>(write_tuple);
            size = get<1>(write_tuple);
            // tag = get<2>(write_tuple);
            tag = object[id].tag; 
        }
        object[id].last_request_point = 0;
        object[id].is_delete = false;
        object[id].tag = tag;
        object[id].size = size;
        for (int j = 1; j <= REP_NUM; j++)
            object[id].unit[j] = static_cast<int*>(malloc(sizeof(int) * (size + 1)));
        do_object_write(id);

        if(Round !=2) putint(id,'\n');
        for (int j = 1; j <= REP_NUM; j++) {
            if(Round !=2) putint(object[id].replica[j],' ');
            for (int k = 1; k <= size; k++) {
                if(Round !=2) putint(object[id].unit[j][k],' ');
            }
            if(Round !=2) putchar('\n');
        }
    }

    if(Round !=2) fflush(stdout);
}